#!/usr/bin/env python

from twisted.conch.telnet import TelnetTransport, Telnet, ProtocolTransportMixin
from twisted.internet.protocol import ClientFactory
from twisted.internet import reactor, defer
from twisted.internet.endpoints import TCP4ClientEndpoint
import twisted.conch.telnet as telnet
from twisted.protocols.basic import LineReceiver
import struct, os, re

AUTH = chr(37)
ENV = chr(39)

class ConnectionStateProtocol(object):
    def __init__(self, State= 'no'):
        self.NeedAnswer = State
        
    def getCurrState(self):
        return self.NeedAnswer
    
    def ChangeState(self):
        if self.NeedAnswer == 'no':
            self.NeedAnser = 'yes'
        elif self.NeedAnser == 'yes':
            self.NeedAnser = 'no'
            
class TelnetEngine(Telnet, ConnectionStateProtocol, ProtocolTransportMixin):
    LocalEnable = [telnet.ECHO, telnet.SGA, telnet.NULL, ENV] #OPCION QUE SE REMUVA DE AQUI NO APARECERA EN US
    LocalDisable = [AUTH]
    
    def __init__(self, State = 'no'):
        ConnectionStateProtocol.__init__(self, State)
        Telnet.__init__(self)
    
    def enableLocal(self, option):
        return option in self.LocalEnable
    
    def CapturAndSendLogin(self):
        print "Capture Login"
        d = defer.Deferred()
        self.login = raw_input()
        self.login = self.login + '\n'
#        print 'Saliendo'
        return d
    
    def applicationDataReceived(self, bytes):
#        Telnet.applicationDataReceived(self, bytes)
        print repr(bytes)
        def myWrite(Data):
            self.write(Data)
            
        echoCMD = self.getOptionState(telnet.ECHO)
#        print d
        if echoCMD.him.state == 'yes':
            if re.search('([Ll]ogin:\s+$)', bytes):
                a = self.CapturAndSendLogin()
                a.addCallback(myWrite)
                a.callback(self.login)
#                self.write('Carlos\n')
    
    def disableRemote(self, option):
        Telnet.disableRemote(self, option)
        return option in self.LocalDisable
    
    def disableLocal(self, option):
        return option in self.LocalDisable
    
    def enableRemote(self, option):
        return True
    
    def dataReceived(self, data):
        Telnet.dataReceived(self, data)
#        print self.negotiationMap
        
    def negotiate(self, bytes):
#        Telnet.negotiate(self, bytes)
        #print repr(bytes)
        class KindOfNeg(object): #EL UPPER PROTOCOL DEBE IR AQUI
            def __init__(self, value):
                self.value = value
        class EnvironOption(KindOfNeg): #PASAR A ENVIRONOPTION COMO PADRE DE ENVIRONPROCEDURE, PARA Q ENVSEND PUEDA LLAMAR A LOS DICTS
            VAR = IS = chr(0)
            VALUE = SEND = chr(1)
            ESC = INFO = chr(2)
            USERVAR = chr(3)
            ReceivePreamble = 'no'                      
            WellKnowVars = {'USER': 'CarlosVM', 'JOB': '', 'ACCT': '', 'PRINTER': '', 'SYSTEMTYPE': 'Linux', 'DISPLAY': os.getenv('DISPLAY', 'None')}
            WindVars = {'SFUTLNTVER': '2', 'SFUTLNTMODE': 'console'}
            def __init__(self, value):
                KindOfNeg.__init__(self, value)
                self.EnvOrder = {self.IS: 'is', self.SEND:self.EnvOptionSend, self.INFO: 'info'}
                self.EnvReq = {self.VAR: self.LookForReq, self.VALUE: 'value', self.ESC: 'esc', self.USERVAR: self.LookForReq}
                self.AllVars = dict(self.WellKnowVars.items() + self.WindVars.items())
        class EnvironProcedure(EnvironOption):
            def __init__(self, value, upper):
                EnvironOption.__init__(self, value)
                self.UpperProto = upper
            def LookForReq(self, req):
                a = []
                pos = 1
                buff = ""
                #print repr(req)
                l = len(req)
                for i in req:
                    if pos == 1 and (i == '\x03' or i == '\x00'):
                        pos +=1
                    elif i == '\x03' or i == '\x00': #COMO AL FINAL NO HAY X03 O X00 NO HACE EL ULTIMO HAPPEN
                        pos +=1
                        a.append(buff)
                        buff = ""
                    elif pos == l:
                        buff +=i
                        a.append(buff)
                    else:
                        pos +=1
                        buff += i
                return a
            def CreteBuffForSend(self, values):
                buf = ''
                buf += struct.pack('c', self.IS)
                for k in values:
                    buf += struct.pack('c', self.USERVAR)
                    buf += k
                    buf += struct.pack('c', self.VALUE)
                    buf += self.AllVars[k]
                return buf
            def EnvOptionSend(self, value = None):
                if value == None:
                    pass
                else:
                    l = self.EnvReq[value[0]](value) #EN ESTE CASO SE METERA LA RESPUEST SOLO CON 0X03, PERO SE TIENE QUE PONER EL ESTANDAR QUE FUE USADO
                    s = self.CreteBuffForSend(l)
                    print repr(s)
#                    self.requestNegotiation(ENV, s)
                    self.UpperProto.requestNegotiation(ENV, s)
        if bytes[0] == ENV:
            env = EnvironProcedure(bytes[0], self)
            if bytes[1] in env.EnvOrder:
                #SI NO SE CONTIENE UN ENVREQ, ES QUE SOLO ESTAMOS EMPEZANDO LA SB Y PONER RECEIVEDPREAMBLE A SI
                if len(bytes) <= 2:
                    #LLAMAMOS A LA FUNCION CON NO AUNQUE DE PASS
                    env.EnvOrder[bytes[1]]()
                else:
                    env.EnvOrder[bytes[1]](bytes[2:])
            else:
                print 'Impossible to handle the Current SB %s' % (repr(bytes[1]))
                self.loseConnection()                  
    
    
    def connectionLost(self, reason):
#        LineReceiver.connectionLost(self, reason)
        Telnet.connectionLost(self, reason)
        
    def telnet_DO(self, option):
        Telnet.telnet_DO(self, option)
        if option == telnet.NAWS:
            lins, cols = self.factory.getTerminalSize()
#            print 'NAWS %d, %d\n' % (lins, cols)
#            print repr(struct.pack('!H', cols)), repr(struct.pack('!H', lins))
            self.requestNegotiation(telnet.NAWS, struct.pack('!HH', cols,lins))
            
    def telnet_WILL(self, option):
        Telnet.telnet_WILL(self, option)
#        print repr(option)
#        s = self.getOptionState(option)
#        print s

class FactoryTelnetEngine(ClientFactory):
    protocol = TelnetEngine
    
    def clientConnectionLost(self, conector, reason):
        print "Hemos perdido conexion con el cliente"
    
    def getTerminalSize(self):
        import os
        env = os.environ
        def ioctl_GWINSZ(fd):
            try:
                import fcntl, termios, struct, os
                cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ,
                                                     '1234'))
            except:
                return
            return cr
        cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2)
        if not cr:
            try:
                fd = os.open(os.ctermid(), os.O_RDONLY)
                cr = ioctl_GWINSZ(fd)
                os.close(fd)
            except:
                pass
            if not cr:
                cr = (env.get('LINES', 25), env.get('COLUMNS', 80))

        ### Use get(key[, default]) instead of a try/catch
        #try:
        #    cr = (env['LINES'], env['COLUMNS'])
        #except:
        #    cr = (25, 80)
        Lineas = int(cr[0])
        Columnas = int(cr[1])
    #return int(cr[1]), int(cr[0])
        return Lineas, Columnas   
        
if __name__ == '__main__':
    from twisted.internet import reactor
    host = '192.168.213.143'
    port = 23
    factory = FactoryTelnetEngine()
    reactor.connectTCP(host, port, factory)
    print 'a correr'
    reactor.run()
